/***
 * * PROJECT
 * *    ATaG
 * * VERSION
 * *    $Revision: 1.2 $
 * * DATE
 * *    $Date: 2008/10/07 12:57:37 $
 * * LOG
 * *    $Log: RandomTaskAssigner.java,v $
 * *    Revision 1.2  2008/10/07 12:57:37  animesh
 * *    removed GPL License [will re-add to all files later]
 * *
 * *    Revision 1.1  2007/06/01 20:53:20  animesh
 * *    bulk update.
 * *
 * *
 * *
 * *   ATaG (Abstract Task Graph) - Data-driven Macroprogramming for
 * *                                Sensor Networks
 * *
 ***/

package atag.compiler;

import atag.common.*;
import atag.runtime.NodeInfo;
import java.util.*;

/**
 * "Sticky" task assignment
 * 
 * @author Animesh
 */
public class RandomTaskAssigner implements TaskAssigner{
    
    private Random randomizer;
    
    /** Creates a new instance of RandomTaskAssigner */
    public RandomTaskAssigner() {
    }
    /**
     * The function that assigns tasks.
     * @param aP The ATaG program
     * @param itP The ITaG version of aP
     * @param nTopo The network, with all nodes
     */
    public void assignTasks(ATaGProgram aP, ITaGProgram itP, ComposedATaG caProgram,
            NetworkTopology nTopo, int randomSeed) {
        
        //TODO initialize the random Number Generator using randomSeed
        this.randomizer = new Random(randomSeed);
        
        // Temporary data structure to store partial task assignments
        HashMap<Integer, Set<Integer>> partialAssignment = new HashMap<Integer, Set<Integer>>();
        Iterator<NodeInfo> itNodes = nTopo.getNodes().iterator();
        while (itNodes.hasNext()) {
            partialAssignment.put(itNodes.next().getMyId(),
                    new HashSet<Integer>());
        }
        
        // first, assign the tasks which are "easy to assign"
        for (ATaGTaskDeclaration at : aP.getTaskList()) {
            
            // Handle External Tasks
            if(at.getFiringRule()[0] == ATaGTaskDeclaration.FIRING_EXTERN)
            {
                continue;
            }
            
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            
            if (instType == ATaGTaskDeclaration.INSTANTIATION_ONE_ON_NODE_ID) {
                // instantiate on that node
                // nTopo.getNodeInfoByID(instParam).assignTask(atID);
                partialAssignment.get(instParam).add(atID);
                at.assign(instParam);
            } else if ((instType == ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE)
            && (instParam == 1)) {
                // instantiate on one of each
                if (at.getNeededAttribute() != null) {
                    // find all nodes with that attribute, and assign this task
                    // on them
                    for (NodeInfo ni : nTopo.getNodesWithAbility(at
                            .getNeededAttribute())) {
                        // ni.assignTask(at.getID());
                        partialAssignment.get(ni.getMyId()).add(at.getID());
                        at.assign(ni.getMyId());
                    }
                } else {
                    // no needed Attribute. Assign on all.
                    for (NodeInfo ni : nTopo.getNodes()) {
//						ni.assignTask(at.getID());
                        partialAssignment.get(ni.getMyId()).add(at.getID());
                        at.assign(ni.getMyId());
                    }
                }
            } else {
                // nothing. Do nothing for this task in first pass
            }
        }
        
        // second pass. Assign tasks that have other instantiation rules
        for (ATaGTaskDeclaration at : aP.getTaskList()) {
            
            // Handle External Tasks
            if(at.getFiringRule()[0] == ATaGTaskDeclaration.FIRING_EXTERN)
            {
                continue;
            }
            
            int instType = at.getLocation()[0];
            int instParam = at.getLocation()[1];
            int atID = at.getID();
            
            if (at.getAssignedTo().size() == 0) {                
                // not assigned yet. Assign now
                switch (instType) {
                    case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE:
                        if (instParam == 1) {
                            // currently dies.. replace with code that handles other
                            // values later
                            System.err.println("Why is task" + at.getName()
                            + "with instantiation type "
                                    + "nodes-per-instance:1 not assigned yet?");
                            System.exit(-5);
                        } else {
                            ArrayList<NodeInfo> targetNodes;
                            if (at.getNeededAttribute() == null) {
                                targetNodes = nTopo.getNodes();
                            } else {
                                // find all nodes with that attribute
                                targetNodes = nTopo.getNodesWithAttribute(at
                                        .getNeededAttribute());
                            }
                            
                            // instantiate the tasks regularly
                            // note the stepsize in the for loop
                            for (int i = 0; i < targetNodes.size(); i = i
                                    + instParam) {
                                NodeInfo ni = targetNodes.get(i);
//							ni.assignTask(at.getID());
                                partialAssignment.get(ni.getMyId()).add(at.getID());
                                at.assign(ni.getMyId());
                            }
                        }
                        break;
                    case ATaGTaskDeclaration.INSTANTIATION_PARTITION_PER_INSTANCE:
                        // Iterate over region-ids of nodes with the same region label                      
                        for (int regionID: nTopo.getRegionIDsInRegion(at.getMyRegionName())) {
                            if(regionID== -1) // Invalid location
                                    continue;
                            ArrayList<NodeInfo> tempTarget = nTopo
                                    .getNodesInRegion(at.getMyRegionName(),
                                    regionID);
                            for (int i = 0; i < instParam; i++) {
                                // assign tasks one by one. Total instParam tasks

//lines below should be changed to the one below
//                                int targetNode = (int) (this.randomizer.nextDouble() * nTopo
//                                        .getNumberOfNodes());
                                int targetNode = (int) (this.randomizer.nextDouble() * 
                                        tempTarget.size());
                                NodeInfo ni = tempTarget.remove(targetNode);
//							ni.assignTask(at.getID());
                                partialAssignment.get(ni.getMyId()).add(at.getID());
                                at.assign(ni.getMyId());
                            }
                        }
                        break;
                    case ATaGTaskDeclaration.INSTANTIATION_ONE_ANYWHERE:
                        int destinationNode;
                        // completely random assignment -- random selects a number
                        // in [0,1)
                        destinationNode = (int) (this.randomizer.nextDouble() * nTopo
                                .getNumberOfNodes());
                        NodeInfo ni = nTopo.getNodeInfoByID(destinationNode);
//					ni.assignTask(at.getID());
                        partialAssignment.get(ni.getMyId()).add(at.getID());
                        at.assign(destinationNode);
                        break;
                    case ATaGTaskDeclaration.INSTANTIATION_NODES_PER_INSTANCE_FRACTIONAL:
                        // do nothing.. allow this to run into default. Fix later
                    default:
                        System.err.println("Illegal instantiation type "
                                + at.getInstType() + " for task" + at.getName());
                        System.exit(-5);
                }
            }
        }
        
        // Assigning tasks to nodes
        itNodes = nTopo.getNodes().iterator();
        while (itNodes.hasNext()) {
            NodeInfo nodeInfo = itNodes.next();
            nodeInfo.assignTasks(partialAssignment.get(nodeInfo.getMyId()));
        }
        
        return;
    }
    
    
}
